﻿using System;
using System.Collections.Generic;
using Core.Scene;
using Cysharp.Threading.Tasks;
using protobuf.Account;
using protobuf.Error;
using protobuf.Protocol;
using UI;
using UnityEngine;

#if UNITY_EDITOR
#endif

namespace Core.Net
{
    /// <summary>
    /// RPC请求管理,RPC请求分为A，B两种协议，A协议需要等待前面所有协议完成后逐个执行，并且一一回调，B协议不需要
    /// </summary>
    public partial class RPCMsgManager
    {
        /// <summary>
        /// 回调函数ID以及对应内容
        /// </summary>
        private Dictionary<int, RPCAsyncResult> callBackList = new Dictionary<int, RPCAsyncResult>(300);

        public const int MSGStartID = 100;
        private static int msgIDGenerater;

        //保存的登录数据Token,用于在恢复连接的时候判断是否可以直接登录
        public string LoginToken = "";

        //10秒为超时

#if UNITY_EDITOR
        public const int TIMEOUT = 10 * 1000;
#else
		public const int TIMEOUT = 15 * 1000;
#endif

        public int GetCallBackListCount()
        {
            return callBackList.Count;
        }

        public RPCMsgManager()
        {
            //绑定回掉事件 
            Account.OnPushKickOutServerCallBackAsync = OnReceivePushKickOffline;
        }

        //从100开始
        int CreateAMSGID()
        {
            //消息ID允许重新循环使用，默认为整数开始
            msgIDGenerater++;
            msgIDGenerater = msgIDGenerater == int.MaxValue ? MSGStartID : msgIDGenerater;
            return msgIDGenerater;
        }

        /// <summary>
        /// RPC实例方法
        /// </summary>
        internal static RPCAsyncResult RPCCallStatic(ProtoType rpcid, Frame frame = null, Delegate action = null, ProtolcalType type = ProtolcalType.Background)
        {
            if (frame == null) frame = new Frame(0);
            return NetManager.inst.RPC.RPCCall(rpcid, frame, action, type);
        }

        /// <summary>
        /// RPC调用
        /// </summary>
        private RPCAsyncResult RPCCall(ProtoType rpcid, Frame frame, Delegate action = null, ProtolcalType type = ProtolcalType.Background)
        {
            var rpcResult = new RPCAsyncResult(action);
            var msgid = CreateAMSGID();
            rpcResult.MSGID = msgid;
            rpcResult.RPCID = rpcid;
            rpcResult.protoType = type;
            rpcResult.isTimeOut = false;
            rpcResult.frame = frame;
            return DealRPCCall(rpcResult);
        }

        /// <summary>
        /// RPC调用
        /// </summary>
        internal RPCAsyncResult DealRPCCall(RPCAsyncResult rpcResult)
        {
            var type = rpcResult.protoType;
            callBackList[rpcResult.MSGID] = rpcResult;

            //如果是在返回登录界面重连中，直接切为失败
            if (NetManager.IsReturningToLogin)
            {
                SetRpcActionConnectFailed(rpcResult);
                return rpcResult;
            }
            if (!NetManager.inst.IsConnected)
            {
                //实时类型的数据通讯，如果网络未连接，则不发送
                if (type == ProtolcalType.RealTime)
                {
                    SetRpcActionConnectFailed(rpcResult);
                    return rpcResult;
                }
                else
                {
                    //如果已经登录过了
                    if (GameManager.inst.IsLogined)
                    {
                        //如果是A、B协议，则开始断线重连,重连后再发送 
                        NetManager.inst.ReconnectWithMessageBoxUntilSuccess(rpcResult, () =>
                        {
                            // 如果是A协议添加超时计时器
                            if (type == ProtolcalType.Ahead)
                            {
                                //A协议发送时候需要先启动动画  
                                NetManager.inst.PRCSendWithAnim(rpcResult);
                            }
                            else
                            {
                                NetManager.inst.SendRPCResult(rpcResult);
                            }
                        });
                    }
                    else //没有登陆过先连接服务器试试
                    {
                        NetManager.inst.ReconnectWithMessageBoxUntilSuccess(rpcResult, () =>
                        {
                            // 如果是A协议添加超时计时器
                            if (type == ProtolcalType.Ahead)
                            {
                                //A协议发送时候需要先启动动画  
                                NetManager.inst.PRCSendWithAnim(rpcResult);
                            }
                            else
                            {
                                NetManager.inst.SendRPCResult(rpcResult);
                            }
                        });
                    }
                }
            }
            else
            {
                // 如果是A协议添加超时计时器
                if (type == ProtolcalType.Ahead)
                {
                    //A协议发送时候需要先启动动画  
                    NetManager.inst.PRCSendWithAnim(rpcResult);
                }
                else
                {
                    NetManager.inst.SendRPCResult(rpcResult);
                }
            }
            return rpcResult;
        }


        /// <summary>
        /// 收到RPC方法,该方法可以考虑使用动态代码生成
        /// </summary>
        public void ReceverRPCCallStatic(int rpcID, int msgID, ByteBuf param)
        {
            //处理服务器远程调用请求
            //DealServerCallback(rpcID, msgID, param);
            //进入服务器广播消息队列，不直接处理
            EnqueueOnPushRPCMsg(rpcID, msgID, param);
            //处理请求回调 
            var rpcErrorType = RPCErrorType.None;
            ErrorModel errorData = null;
            if (rpcID == (int) ProtoType.Error)
            {
                rpcErrorType = RPCErrorType.CustomDefineError;
                errorData = param.ReadObject<ErrorModel>();
            }
            if (callBackList.TryGetValue(msgID, out var action))
            {
                //callBackList.Remove(msgID);
                action.isCompleted = true;
                action.param = param;
                NetManager.inst.TimerSystem.DestroyTimer(action.timerID);
                action.errorData = errorData;
                action.errorType = rpcErrorType;
                //处理RPC请求回调 
                DealRPCCallBack(action);
                action.Finish();
                action.receiveTime = NetHelper.GetEnvironmentTickCount();
                //LOG
#if UNITY_EDITOR || DEVELOPMENT_BUILD
                Debug.LogWarning($"RPC处理成功rpcID={action.RPCID.ToString()}，msgID={action.MSGID},用时={action.receiveTime - action.sendTime}, 当前时间：{NetManager.inst.ServerTime.ToLocalTime().ToString()}");
#endif
                //如果包体内容没有被消耗，消耗包体内容
                if (action.frame != null && !action.frame.IsConsumed)
                {
                    action.frame.Consumed();
                }
            }
        }

        /// <summary>
        /// 重置已经完成的RPC请求为未完成状态
        /// </summary>
        /// <param name="res"></param>
        internal void ResetRPCToUncompleted(RPCAsyncResult res)
        {
            res.isCompleted = false;
            res.isTimeOut = false;
            res.errorType = RPCErrorType.None;
            res.timerID = 0;
        }

        /// <summary>
        /// 通用处理RPC错误请求,判断哪些请求结果需要重发，如果返回值为False，表示放弃该条请求回掉
        /// </summary>
        /// <returns></returns>
        bool RPCCallBackErrorFilter(RPCAsyncResult res)
        {
            // 通用错误处理
            if (res.errorType != RPCErrorType.None)
            {
                if (res.protoType != ProtolcalType.RealTime)
                {
                }
            }
            // A协议弹出窗口处理连接问题
            if (res.protoType == ProtolcalType.Ahead)
            {
                // 处理连接失败、超时的情况
                if (res.errorType == RPCErrorType.TimeOut)
                {
                    //因为重新连接需要重发，所以过滤原请求
                    return false;
                }
                //连接失败，没有什么特殊处理
                else if (res.errorType == RPCErrorType.ClearRPC)
                {
                } 
                else if (res.errorType == RPCErrorType.CustomDefineError)
                {
                }
            }
            return res.errorType == RPCErrorType.None;
        }

        /// <summary>
        /// 处理网络关闭,清除所有未收到的回掉
        /// </summary>
        public void ClearRPC(RPCAsyncResult exceptClearRpcResult = null)
        {
            List<RPCAsyncResult> results = new List<RPCAsyncResult>(callBackList.Values);
            foreach (var rpcResult in results)
            {
                if (rpcResult != exceptClearRpcResult)
                {
                    //设置RPC失败 ，然后统一清空
                    //原先函数全部请求失败
                    SetRpcActionConnectFailed(rpcResult);
                }
            }
            callBackList.Clear();
            if (exceptClearRpcResult != null)
            {
                callBackList[exceptClearRpcResult.MSGID] = exceptClearRpcResult;
            }
        }

        public void ClearLoginToken()
        {
            //清除登录Token
            LoginToken = null;
        }

        /// <summary>
        /// 当网络连接成功，发送Token，重传内部数据
        /// </summary>
        public RPCAsyncResult DoNetReconnect()
        {
            //断线连接建立，立刻发送Header，后台创建身份
            if (!string.IsNullOrEmpty(LoginToken))
            {
                //发送Token重建连接,重做重连流程。
                return Account.LoginTokenCall(new protobuf.Account.ReqLoginTokenModel() {Token = LoginToken}, null);
            }
            //没有登录TOKEN意味着重连可以直接放行
            return null;
        }

        //设置RPC请求失败
        internal void SetRpcActionConnectFailed(RPCAsyncResult rpcResult)
        {
            //原先函数全部请求失败
            rpcResult.errorType = RPCErrorType.ClearRPC;
            rpcResult.isCompleted = true;
            //处理RPC请求回调，
            DealRPCCallBack(rpcResult);
            rpcResult.Finish();
        }

        internal void DealRPCCallBack(RPCAsyncResult action)
        {
            //过滤重发等错误请求
            if (RPCCallBackErrorFilter(action))
            {
                callBackList.Remove(action.MSGID);
                //该方法位于RPCCallBackGen文件中，为自动生成代码
                DealRPCCallBackGen(action);
            }
            NetManager.inst.OnAfterRPCCallBack(action);
        }

        #region 服务器推送踢掉下线

        internal async UniTask OnReceivePushKickOffline(RespPushKickOutModel resp)
        {
            UIManager.inst.tips.ShowTips("收到的服务器推送踢掉下线消息");
            SceneManager.inst.currentType.Value = SceneType.None;
            await SceneManager.inst.LoadSceneAsync(SceneType.Login, SceneManager.LOGIN_SCENE_NAME);
            // NetManager.inst.HandleRPCCustomACKErrorCode(null, (uint)resp.Error.Type);
        }

        #endregion
    }
}